package com.hivekion.common.security;

import com.hivekion.common.entity.vo.LoginUser;
import com.hivekion.common.exception.BusinessException;
import com.hivekion.system.service.ISysUserService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT工具类
 *
 * @author Louis
 * @date Jun 29, 2019
 */
public class JwtTokenUtils {

      public static final String TOKEN_PREFIX = "Bearer "; // 前缀
      public static final String AUTHORIZATION = "Authorization"; // 表头授权
      private static final long serialVersionUID = 1L;
      private static final String USERNAME = Claims.SUBJECT; // 用户名称
      private static final String USERID = Claims.ID; // 用户ID
      private static final String CREATED = "created"; // 创建时间
      private static final String AUTHORITIES = "authorities"; // 权限列表
      private static final String SECRET = "1q2w3E*"; // 密钥
      private static final long EXPIRE_TIME = 12 * 60 * 60 * 1000; // 有效期12小时
      @Autowired
      ISysUserService userService;

      /**
       * 生成令牌
       *
       * @return 令牌
       */
      public static String generateToken(Authentication authentication) {
	    Map<String, Object> claims = new HashMap<>(3);
	    LoginUser user = SecurityUtils.getLoginUser(authentication);
	    claims.put(USERNAME, user.getUsername());
	    claims.put(CREATED, new Date());

	    Date expirationDate = new Date(System.currentTimeMillis() + EXPIRE_TIME);
	    return Jwts.builder()
		    .setClaims(claims) //
		    .setExpiration(expirationDate) // 过期时间
		    .signWith(SignatureAlgorithm.HS512, SECRET)
		    .compact();
      }

      /**
       * 从数据声明生成令牌
       *
       * @param claims 数据声明
       * @return 令牌
       */
      private static String generateToken(Map<String, Object> claims) {
	    Date expirationDate = new Date(System.currentTimeMillis() + EXPIRE_TIME);
	    return Jwts.builder()
		    .setClaims(claims) //
		    .setExpiration(expirationDate) // 过期时间
		    .signWith(SignatureAlgorithm.HS512, SECRET)
		    .compact();
      }

      /**
       * 从令牌中获取用户名
       *
       * @param token 令牌
       * @return 用户名
       */
      public static String getUsernameFromToken(String token) {
	    String username;
	    try {
		  Claims claims = getClaimsFromToken(token);
		  username = claims.getSubject();
	    } catch (Exception e) {
		  username = null;
	    }
	    return username;
      }

      /**
       * 根据请求令牌获取登录认证信息
       *
       * @return 用户名
       */
      public static Authentication getAuthenticationeFromToken(HttpServletRequest request) {
	    Authentication authentication = null;
	    String token = JwtTokenUtils.getToken(request); // 获取请求携带的令牌
	    if (token == null) {
		  throw new BusinessException(500, "未找到授权信息");
	    }
	    if (token != null) {
		  if (SecurityUtils.getAuthentication() == null) {
			Claims claims = getClaimsFromToken(token); // 上下文中Authentication为空
			if (claims == null) {
			      return null;
			}
			String username = claims.getSubject();
			if (username == null) {
			      return null;
			}
			if (isTokenExpired(token)) {
			      return null;
			}
			Object authors = claims.get(AUTHORITIES); // 权限列表
			LoginUser user = new LoginUser();
			authentication =
				new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
		  } else {
			if (validateToken(token, SecurityUtils.getUsername())) {
			      authentication =
				      SecurityUtils
					      .getAuthentication(); // 如果上下文中Authentication非空，且请求令牌合法，直接返回当前登录认证信息
			}
		  }
	    }
	    return authentication;
      }

      /**
       * 从令牌中获取数据声明
       *
       * @param token 令牌
       * @return 数据声明
       */
      private static Claims getClaimsFromToken(String token) {
	    Claims claims;
	    try {
		  claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
	    } catch (Exception e) {
		  claims = null;
	    }
	    return claims;
      }

      /**
       * 验证令牌
       *
       * @param token
       * @param username
       * @return
       */
      public static Boolean validateToken(String token, String username) {
	    String userName = getUsernameFromToken(token);
	    return (userName.equals(username) && !isTokenExpired(token));
      }

      /**
       * 刷新令牌
       *
       * @param token
       * @return
       */
      public static String refreshToken(String token) {
	    String refreshedToken;
	    try {
		  Claims claims = getClaimsFromToken(token);
		  claims.put(CREATED, new Date());
		  refreshedToken = generateToken(claims);
	    } catch (Exception e) {
		  refreshedToken = null;
	    }
	    return refreshedToken;
      }

      /**
       * 判断令牌是否过期
       *
       * @param token 令牌
       * @return 是否过期
       */
      public static Boolean isTokenExpired(String token) {
	    try {
		  Claims claims = getClaimsFromToken(token);
		  Date expiration = claims.getExpiration();
		  return expiration.before(new Date());
	    } catch (Exception e) {
		  return false;
	    }
      }

      /**
       * 获取请求token
       *
       * @param request
       * @return
       */
      public static String getToken(HttpServletRequest request) {
	    String token = request.getHeader("Authorization");
	    String token2 = request.getHeader("BASE_TOKEN");
	    String tokenHead = "Bearer ";
	    if (token == null) {
		  token = request.getHeader("token");
	    } else if (token.contains(tokenHead)) {
		  token = token.substring(tokenHead.length());
	    }
	    if ("".equals(token)) {
		  token = null;
	    }
	    return token;
      }
}
